package com.own.business.sync.trigger.base.aspect;

import com.own.business.sync.trigger.base.aop.SyncTriggerAop;
import com.own.business.sync.trigger.base.config.SyncTriggerCondition;
import com.own.business.sync.trigger.base.util.SyncTriggerUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Conditional;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * SyncTriggerAop
 *
 * @author chenxueli
 * @date 2024-01-08 16:16:00
 */
@Slf4j
@Aspect
@Component
@Conditional(SyncTriggerCondition.class)
public class SyncTriggerAspect {

    private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private static final DefaultParameterNameDiscoverer DEFAULT_PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
    @Resource
    private SyncTriggerUtil syncTriggerUtil;

    @Pointcut("@annotation(com.own.business.sync.trigger.base.aop.SyncTriggerAop)")
    public void aspect() {
    }

    /**
     * 成功执行完成的时候触发
     *
     * @param point  切点
     * @param data   注解
     * @param result 返回值
     */
    @AfterReturning(value = "aspect() && @annotation(data) ", returning = "result")
    public void afterReturning(JoinPoint point, SyncTriggerAop data, Object result) {
        var targetId = getTargetId(data, point, result);
        var oldStatus = getOldStatus(data, point, result);
        var newStatus = getNewStatus(data, point, result);
        syncTriggerUtil.add(targetId, data.type(), data.moduleName(), oldStatus, newStatus);
    }

    /**
     * 获取目标id
     *
     * @param data   注解
     * @param result 返回值
     * @return 目标id
     */
    private Long getTargetId(SyncTriggerAop data, JoinPoint point, Object result) {
        if (StringUtils.isNotBlank(data.targetIdSpel())) {
            var targetIdText = getSpelValue(data.targetIdSpel(), point, result);
            if (StringUtils.isNotBlank(targetIdText)) {
                return Long.parseLong(targetIdText);
            }
        }
        return data.targetId();
    }

    /**
     * 获取旧状态
     *
     * @param data   注解
     * @param result 返回值
     * @return 旧状态
     */
    private Integer getOldStatus(SyncTriggerAop data, JoinPoint point, Object result) {
        if (StringUtils.isNotBlank(data.oldStatusSpel())) {
            var oldStatusText = getSpelValue(data.oldStatusSpel(), point, result);
            if (StringUtils.isNotBlank(oldStatusText)) {
                return Integer.parseInt(oldStatusText);
            }
        }
        return data.oldStatus();
    }

    /**
     * 获取新状态
     *
     * @param data   注解
     * @param result 返回值
     * @return 新状态
     */
    private Integer getNewStatus(SyncTriggerAop data, JoinPoint point, Object result) {
        if (StringUtils.isNotBlank(data.newStatusSpel())) {
            var newStatusText = getSpelValue(data.newStatusSpel(), point, result);
            if (StringUtils.isNotBlank(newStatusText)) {
                return Integer.parseInt(newStatusText);
            }
        }
        return data.newStatus();
    }

    /**
     * 获取spel表达式的值
     *
     * @param expressionStr 表达式
     * @param result        参数
     * @return 值
     */
    private String getSpelValue(String expressionStr, JoinPoint point, Object result) {
        var methodSignature = (MethodSignature) point.getSignature();
        var paramNames = DEFAULT_PARAMETER_NAME_DISCOVERER.getParameterNames(methodSignature.getMethod());
        if (paramNames == null) {
            return "";
        }
        var expression = SPEL_EXPRESSION_PARSER.parseExpression(expressionStr);
        var context = new StandardEvaluationContext();
        var args = point.getArgs();
        for (int i = 0; i < args.length; i++) {
            context.setVariable(paramNames[i], args[i]);
        }
        context.setVariable("result", result);
        var value = expression.getValue(context);
        return value == null ? "" : value.toString();
    }

}
